﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography;
using DotNetUtilityLibrary;
using System.Threading;
using System.Security;

namespace CryptographyDemonstration
{
	public partial class MainForm : Form
	{

		#region Fields

		private RSACryptoServiceProvider mProvider;
		private int mMaxMessageLength
		{
			get
			{
				return (((mProvider.KeySize - 384) / 8) + 7) / 2;
			}
		}

		#endregion Fields

		#region Constructor

		public MainForm()
		{
			InitializeComponent();
		}

		#endregion Constructor

		#region Event Handlers

		private void txtPlainText_TextChanged(object sender, EventArgs e)
		{
			lblMessageTextLength.Text = string.Format("Message Text Length: {0} / {1}",
				txtPlainText.Text.Length,
				(((mProvider.KeySize - 384) / 8) + 7) / 2);
			Encrypt();
		}

		private void btnValidatePublicKey_Click(object sender, EventArgs e)
		{
			if (mProvider != null)
			{
				mProvider.Dispose();
				mProvider = null;
				TogglePlainTextTextBox(false);
				TogglePublicKeyTextBox(true);
			}
			else
			{
				try
				{
					mProvider = new RSACryptoServiceProvider();
					mProvider.PersistKeyInCsp = false;
					mProvider.FromXmlString(txtPublicKeyInformation.Text);
					TogglePlainTextTextBox(true);
					TogglePublicKeyTextBox(false);
					Encrypt();
				}
				catch (XmlSyntaxException)
				{
					MessageBox.Show(
@"Failed to parse XML contents.
Please validate the XML representation of the public key.",
						"XML Parsing Exception Encountered",
						MessageBoxButtons.OK,
						MessageBoxIcon.Error);
					if (mProvider != null)
					{
						mProvider.Dispose();
						TogglePlainTextTextBox(false);
						TogglePublicKeyTextBox(true);
					}
				}
				catch (FormatException)
				{
					MessageBox.Show(
@"Invalid public key values supplied.
Please verify the validity of the public key information.",
					"Public Key Parsing Exception Encountered",
					MessageBoxButtons.OK,
					MessageBoxIcon.Error);
					if (mProvider != null)
					{
						mProvider.Dispose();
						TogglePlainTextTextBox(false);
						TogglePublicKeyTextBox(true);
					}
				}
			}
		}

		#endregion Event Handlers

		#region Private Methods

		private void UpdateKeyInformation()
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(() => UpdateKeyInformation()));
				return;
			}

			txtPublicKeyInformation.Text =
				XMLHelper.FormatXml(mProvider.ToXmlString(false));
		}

		private void StallForKeyGeneration()
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(() => StallForKeyGeneration()));
				return;
			}

			txtPublicKeyInformation.Text =
				"Generating a new key pair...";
		}

		private void TogglePlainTextTextBox(bool toggle)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(() => TogglePlainTextTextBox(toggle)));
				return;
			}

			txtPlainText.Enabled = toggle;
			txtEncryptedText.Enabled = toggle;
			if (toggle)
			{
				txtPlainText.BackColor = SystemColors.Window;
			}
			else
			{
				txtPlainText.BackColor = SystemColors.ControlLight;
			}
		}

		private void TogglePublicKeyTextBox(bool toggle)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(() => TogglePlainTextTextBox(toggle)));
				return;
			}

			txtPublicKeyInformation.Enabled = toggle;
			if (toggle)
			{
				txtPublicKeyInformation.BackColor = SystemColors.Window;
				btnValidatePublicKey.Text = "Validate Public Key";
			}
			else
			{
				txtPublicKeyInformation.BackColor = SystemColors.ControlLight;
				btnValidatePublicKey.Text = "Choose Another Public Key";
			}
		}

		public void UpdateEncryptedText(string encryptedText)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(() => UpdateEncryptedText(encryptedText)));
				return;
			}
			txtEncryptedText.Text = encryptedText;
		}

		public void RevertPlainText()
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(() => RevertPlainText()));
				return;
			}

			txtPlainText.Text = txtPlainText.Text.Substring(0, mMaxMessageLength);
			txtPlainText.Select(txtPlainText.Text.Length, 0);
		}

		private void Encrypt()
		{
			try
			{
				// Wrap up a change the CryptographyHelper with ConvertHelper calls to
				// transparently change the displaying functionality
				string encryptedText =
					ConvertHelper.BytesToHexString(
						ConvertHelper.StringToBytes(
							CryptographyHelper.AsymmetricEncrypt(mProvider, txtPlainText.Text)
						)
					);
				UpdateEncryptedText(encryptedText);
			}
			catch (CryptographicException)
			{
				MessageBox.Show(
					string.Format(
@"The length of the message is too long.
A {0} bit key can only encrypt {1} characters.",
						mProvider.KeySize,
						mMaxMessageLength),
					"Cryptographic Exception Encountered",
					MessageBoxButtons.OK,
					MessageBoxIcon.Error);
				RevertPlainText();
			}
		}

		#endregion Private Methods

	}
}
